﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.ComponentModel;
using System.Text;
using System.Drawing;
using Asol.Reporting.Editor.Editor;
using Asol.Reporting.Editor.Components;
using System.Windows.Forms;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using Asol.Reporting.Editor.ComponentsLayer;
using Asol.Reporting.Support;

namespace Asol.Reporting.Editor.Data
{
    // class ReportComponent : nositel objektu reportu
    /// <summary>
    /// ReportComponent : nositel objektu reportu.
    /// Ze strany editoru jde o objekt, který je editován v ploše, a je evidován v dokumentu.
    /// Tento objekt v sobě ale nenese konkrétní vlastnosti objektu sestavy (font, text, proměnné, atd);
    /// tyto vlastnosti řeší instance IReportObject = konkrétní objekt.
    /// </summary>
    public class ReportComponent : ReportItem, IRevertItem
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="document"></param>
        /// <param name="reportObject"></param>
        public ReportComponent(ReportDocument document, IReportObject reportObject)
        {
            this.ComponentId = ++_ComponentId;
            this._Document = document;
            this.ReportObject = reportObject;
            this.LayerStandard = EditorLayer.Components;
            this.LayerInteractive = EditorLayer.Interactive;
            this.AnchorX = AnchorType.Begin;
            this.AnchorY = AnchorType.Begin;
            this.RelativeBounds = RelativeRectangleF.Empty;

            reportObject.Owner = this;    // Pokud možno setovat jako poslední, poté kdy je navázán Document i this.ReportObject, protože IReportObject.set_Owner může chtít pracovat s dokumentem atd.
        }
        /// <summary>
        /// Objekt reportu, konkrétní třída
        /// </summary>
        public IReportObject ReportObject { get; private set; }
        /// <summary>
        /// ID komponenty.
        /// Nové komponenty dostávají vždy nové ID.
        /// </summary>
        public int ComponentId { get; private set; }
        /// <summary>
        /// Resetuje čítač ID. Volat pouze po Clear celého dokumentu.
        /// </summary>
        public static void ResetId()
        {
            _ComponentId = 0;
        }
        /// <summary>
        /// Autoincrement ID
        /// </summary>
        private static int _ComponentId;
        #endregion
        #region Vazba komponenty na datové struktury dokumentu (OwnerBand, Document)
        /// <summary>
        /// Band (svazek), do něhož patří tato komponenta svým horním okrajem.
        /// Můj hostitel.
        /// Lze jej čas od času měnit (= přesunout this prvek někam jinam), možno i kopírovat do jiného parenta.
        /// </summary>
        public ReportBand OwnerTopBand
        {
            get
            {
                if (this._Document == null || this._OwnerTopBandId == 0)
                    return null;
                return this._Document.GetBand(this._OwnerTopBandId);
            }
            internal set
            {
                this._OwnerTopBandId = (value == null ? 0 : value.BandId);
            }
        }
        /// <summary>
        /// Band (svazek), do něhož patří tato komponenta svým dolním okrajem.
        /// Pokud tato komponenta nepodporuje MultiBand (EnableMultiBand je false), pak je zde OwnerTopBand.
        /// Lze jej čas od času měnit (= přesunout this prvek někam jinam), možno i kopírovat do jiného parenta.
        /// </summary>
        public ReportBand OwnerBottomBand
        {
            get
            {
                if (!this.EnableMultiBand || this._OwnerBottomBandId == 0)
                    return this.OwnerTopBand;
                if (this._Document == null || this._OwnerBottomBandId == 0)
                    return null;
                return this._Document.GetBand(this._OwnerBottomBandId);
            }
            internal set
            {
                if (this.EnableMultiBand)
                    this._OwnerBottomBandId = (value == null ? 0 : value.BandId);
            }
        }
        /// <summary>
        /// Jméno owner bandu (na horním okraji) = cesta od rootu ke konkrétnímu bandu, aktuálně vyhodnocená
        /// </summary>
        public string OwnerTopBandName
        {
            get
            {
                ReportBand ownerBand = this.OwnerTopBand;
                if (ownerBand == null) return "";
                return ownerBand.FullHeaderText;
            }
        }
        /// <summary>
        /// Jméno owner bandu (na dolním okraji) = cesta od rootu ke konkrétnímu bandu, aktuálně vyhodnocená
        /// </summary>
        public string OwnerBottomBandName
        {
            get
            {
                ReportBand ownerBand = this.OwnerBottomBand;
                if (ownerBand == null) return "";
                return ownerBand.FullHeaderText;
            }
        }
        /// <summary>
        /// Jméno owner bandu (Top) načtené z XML, 
        /// slouží k zatřídění komponenty do odpovídajícího Bandu po načtení z XML do Reportu, typicky v Paste.
        /// </summary>
        internal string XmlOwnerTopBandName { get; set; }
        /// <summary>
        /// Jméno owner bandu (Bottom) načtené z XML, 
        /// slouží k zatřídění komponenty do odpovídajícího Bandu po načtení z XML do Reportu, typicky v Paste.
        /// </summary>
        internal string XmlOwnerBottomBandName { get; set; }
        /// <summary>
        /// ID Bandu (svazek), do něhož patří tato komponenta (svým horním okrajem).
        /// Pokud komponenta nemá OwnerTopBand, je zde 0.
        /// Změnou příslušnosti do Bandu se změní i OwnerTopBandId.
        /// </summary>
        public int OwnerTopBandId
        {
            get { return this._OwnerTopBandId; }
            internal set { this._OwnerTopBandId = value; }
        }
        /// <summary>
        /// ID Bandu (svazek), do něhož patří tato komponenta (svým dolním okrajem).
        /// Pokud komponenta nemá OwnerBottomBand, je zde 0.
        /// Změnou příslušnosti do Bandu se změní i OwnerBottomBandId.
        /// </summary>
        public int OwnerBottomBandId
        {
            get { return this._OwnerBottomBandId; }
            internal set { this._OwnerBottomBandId = value; }
        }
        /// <summary>
        /// Příznak (true), že tato komponenta je přes více bandů (má to povolené a liší se její TopBand od BottomBand).
        /// </summary>
        public bool IsMultiBand
        {
            get { return (this.EnableMultiBand && this._OwnerTopBandId != 0 && this._OwnerBottomBandId != 0 && this._OwnerTopBandId != this._OwnerBottomBandId); }
        }
        /// <summary>
        /// Dokument, do něhož (prostřednictvím OwnerBand) patří tato komponenta.
        /// </summary>
        public ReportDocument Document
        {
            get { return this._Document; }
            private set { this._Document = value; }
        }
        /// <summary>Úložiště pro BandID, do něhož tato komponenta patří (svým horním okrajem).</summary>
        protected int _OwnerTopBandId;
        /// <summary>Úložiště pro BandID, do něhož tato komponenta patří (svým dolním okrajem).</summary>
        protected int _OwnerBottomBandId;
        /// <summary>Úložiště pro ReportDocument, do něhož tato komponenta patří.</summary>
        protected ReportDocument _Document;
        #endregion
        #region Souřadnice komponenty (VisualBounds)
        /// <summary>
        /// Resetuje svoje cachované VisualBounds
        /// </summary>
        public override void ResetVisualBounds()
        {
            base.ResetVisualBounds();
        }
        /// <summary>
        /// Metoda vrátí logické souřadnice (v milimetrech), které odpovídají zadaným souřadnicím vizuálním z editoru (v pixelech)
        /// </summary>
        /// <param name="visualBound"></param>
        /// <returns></returns>
        internal RectangleF GetLogicalBoundsFromEditor(RectangleF visualBound)
        {
            RectangleF logicalArea = this.ViewPortConvertor.LogicalFromVisual(visualBound);
            logicalArea.Y -= this.OwnerTopBand.EditorLogicalTop;
            return logicalArea;
        }
        /// <summary>
        /// <para>
        /// Vrátí vizuální prostor tohoto prvku, v pixelech.
        /// Typicky by měla určit vizuální souřadnice na základě souřadnic logických.
        /// </para>
        /// <para>
        /// Pokud vrátí null, pak prvek není viditelný.
        /// Metoda je volána pouze jedenkrát v rámci Draw().
        /// Vrácená hodnota bude uložena do this.CurrentVisualBound.
        /// Pokud bude vráceno null, neprovede se kreslení ani další operace (deklarace segmentů).
        /// </para>
        /// </summary>
        protected override RectangleF GetVisualBound(EditEventArgs args)
        {
            // Pokud právě probíhá editace rozměru, pak se prvek nachází na editovaných souřadnicích:
            if (this.EditedCurrentVisualArea.HasValue)
                return this.EditedCurrentVisualArea.Value;

            // Pokud je už určena vizuální souřadnice, pak ji vrátím:
            if (this.StableCurrentVisualArea.HasValue)
                return this.StableCurrentVisualArea.Value;

            // Mimo editaci je nutno vypočítat vizuální Bound a uložit je do cache:
            RectangleF topArea = this.OwnerTopBand.CurrentRowVisualBound.Value;
            RectangleF bottomArea = this.OwnerBottomBand.CurrentRowVisualBound.Value;
            RectangleF visualArea = this.RelativeBounds.GetVisual(topArea, bottomArea, this.AnchorX, this.AnchorY, args.ViewPortConvertor);
            this.StableCurrentVisualArea = visualArea;
            return visualArea;
        }
        /// <summary>
        /// Umístí tuto komponentu na danou vizuální souřadnici.
        /// Součástí tohoto procesu je vyhledání odpovídajícího Bound podle pozice komponenty, 
        /// a její uložení do this.OwnerTopBand a this.OwnerBottomBand (podle potřeby).
        /// </summary>
        /// <param name="visualArea"></param>
        public void SetVisualBound(RectangleF visualArea)
        {
            EditEventArgs args = new EditEventArgs(this.Document.EditorControl.ViewPortConvertor, this.Document.EditorControl.ObjectState);
            this.SetVisualBound(args, visualArea);
        }
        /// <summary>
        /// Nastaví vizuální prostor tohoto prvku.
        /// Typicky by měla na základě předaných vizuálních souřadnic určit souřadnice logické a někam do sebe si je uchovat.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="visualArea"></param>
        protected override void SetVisualBound(EditEventArgs args, RectangleF visualArea)
        {
            base.SetVisualBound(args, visualArea);

            // Vyhledání Bandu podle vizuálního bodu (v prostoru komponent):
            PointF point = visualArea.Location;
            ReportBand ownerTopBand = this.Document.FindBandOnVisualPoint(point);
            if (ownerTopBand != null)
                this.OwnerTopBand = ownerTopBand;
            if (this.EnableMultiBand)
            {
                point = new PointF(visualArea.X, visualArea.Bottom);
                ReportBand ownerBottomBand = this.Document.FindBandOnVisualPoint(point);
                if (ownerBottomBand != null)
                    this.OwnerBottomBand = ownerBottomBand;
            }

            // Určit souřadnice (this.RelativeBounds) na základě vizuálních souřadnic:
            RectangleF topArea = this.OwnerTopBand.CurrentRowVisualBound.Value;
            RectangleF bottomArea = this.OwnerBottomBand.CurrentRowVisualBound.Value;
            this.RelativeBounds = RelativeRectangleF.CreateLogical(visualArea, topArea, bottomArea, args.ViewPortConvertor);
            this.StableCurrentVisualArea = visualArea;
        }
        /// <summary>
        /// Pracovní reference na IViewPortConvertor (před this.Document se dostanu na jeho EditorControl, a ten má property ViewPortConvertor)
        /// </summary>
        protected IViewPortConvertor ViewPortConvertor { get { return this.Document.EditorControl.ViewPortConvertor; } }
        #endregion
        #region Overrides od EditorItem: InteractivityMode, PrepareSegments, DrawItem
        /// <summary>
        /// Interaktivitu tohoto prvku deklaruje ReportObject.
        /// Setování této hodnoty zde nemá význam.
        /// </summary>
        public override EditItemInteractivityMode InteractivityMode
        {
            get { return this.ReportObject.InteractivityMode; }
            set { }
        }
        /// <summary>
        /// True, pokud tento objekt má být zobrazen na interaktivní vrstvě.
        /// Rozdíl proti bázové třídě EditorItem je ve stavu OnControl: bázová třída jej nepovažuje za interaktivní, ale my ano.
        /// true : pokud objekt je IsWithMouseDown nebo IsWithMouseInteractive nebo OnControl
        /// (CurrentMouseState je MouseLeftDown nebo MouseRightDown nebo MouseDragMove nebo MouseDragResize)
        /// false : pokud není myš stisknuta (CurrentMouseState je Outside nebo None)
        /// </summary>
        public override bool IsOnInteractiveLayer { get { return (this.IsWithMouseDown || this.IsWithMouseInteractive || this.IsWithMouse); } }
        /// <summary>
        /// Klíčová metoda pro řízení veškeré interaktivity prvku.
        /// Komponenta umožní svému ReportObjectu, aby vyřešil interaktivní události podle své libosti,
        /// ale na druhou stranu ReportObject nic řešit nemusí a pak se předá řízení do base třídy.
        /// </summary>
        /// <param name="args"></param>
        public override void MouseChange(EditInteractiveEventArgs args)
        {
            // Komponenta (ReportComponent) je nosičem prvku ReportObject, předá tedy řízení události do něj:
            EditorItemSegment onMouseSegment = this.FindActiveSegment(args.MousePointF);
            ReportObjectInteractiveSegmentsArgs objArgs = new ReportObjectInteractiveSegmentsArgs(args, this, onMouseSegment);
            this.ReportObject.EditorSegmentInteractivity(objArgs);

            if (objArgs.Handled)
            {   // Pokud ReportObject událost ošetřil, převezmeme výsledky, a nebudeme spouštět základní metodu:
                objArgs.StoreResultTo(args);
            }
            else
            {   // Výchozí řízení události:
                base.MouseChange(args);
            }
        }
        /// <summary>
        /// Připraví data pro všechny interaktivní prvky tohoto prvku.
        /// </summary>
        /// <param name="args"></param>
        protected override void PrepareSegments(EditPaintEventArgs args)
        {
            if (!this.CurrentIsVisible) return;

            ReportObjectPrepareSegmentsArgs objArgs = new ReportObjectPrepareSegmentsArgs(args, this);
            this.ReportObject.EditorPrepareInteractiveSegments(objArgs);

            this.AllSegmentList = objArgs.Segments;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        protected override void DrawItem(EditPaintEventArgs args)
        {
            if (!this.CurrentIsVisible) return;

            ReportObjectDrawArgs objArgs = new ReportObjectDrawArgs(args, this);
            
            this.ReportObject.EditorPaint(objArgs);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši se stisklo a uvolnilo, mezitím se nepohnulo. Klasický LeftClick.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftClick(EditInteractiveEventArgs args)
        {
            // Komponenta (ReportComponent) je nosičem prvku ReportObject, předá tedy řízení události do něj:
            EditorItemSegment onMouseSegment = this.FindActiveSegment(args.MousePointF);
            ReportObjectInteractiveSegmentsArgs objArgs = new ReportObjectInteractiveSegmentsArgs(args, this, onMouseSegment);
            this.ReportObject.EditorSegmentInteractivity(objArgs);

            if (objArgs.Handled)
            {   // Pokud ReportObject událost ošetřil, převezmeme výsledky, a nebudeme spouštět základní metodu:
                objArgs.StoreResultTo(args);
            }
            else
            {   // Pokud komponenta událost neobsloužila, obsloužíme se zde:
                switch (args.ObjectState)
                {
                    case EditorObjectState.Select:
                        this.LeftClickSelectDetect(args, onMouseSegment);
                        break;
                    case EditorObjectState.Anchor:
                        this.LeftClickAnchorDetect(args, onMouseSegment);
                        break;
                }
            }
        }
        /// <summary>
        /// Řeší kliknutí na segment v režimu Select
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void LeftClickSelectDetect(EditInteractiveEventArgs args, EditorItemSegment segment)
        {
            if (segment != null && segment.GripType == EditorItemSegmentGripType.Body && !this.IsLocked)
                this.SelectComponentOne(Control.ModifierKeys == Keys.Control);
        }
        /// <summary>
        /// Řeší kliknutí na segment v režimu Anchor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected void LeftClickAnchorDetect(EditInteractiveEventArgs args, EditorItemSegment segment)
        {
            if (segment == null) return;
            switch (segment.GripType)
            {
                case EditorItemSegmentGripType.TopCenter:
                    this.AnchorY = AnchorChange(this.AnchorY, AnchorType.Begin);
                    args.ReDrawLayer(this.CurrentLayer);
                    break;
                case EditorItemSegmentGripType.BottomCenter:
                    this.AnchorY = AnchorChange(this.AnchorY, AnchorType.After);
                    args.ReDrawLayer(this.CurrentLayer);
                    break;
                case EditorItemSegmentGripType.MiddleLeft:
                    this.AnchorX = AnchorChange(this.AnchorX, AnchorType.Begin);
                    args.ReDrawLayer(this.CurrentLayer);
                    break;
                case EditorItemSegmentGripType.MiddleRight:
                    this.AnchorX = AnchorChange(this.AnchorX, AnchorType.After);
                    args.ReDrawLayer(this.CurrentLayer);
                    break;
            }
        }
        /// <summary>
        /// Vrátí kotvu Anchor, která vznikne kliknutím na určitou stranu kotvy aktuální.
        /// </summary>
        /// <param name="anchorCurrent"></param>
        /// <param name="anchorClick"></param>
        /// <returns></returns>
        private AnchorType AnchorChange(AnchorType anchorCurrent, AnchorType anchorClick)
        {   // Využijeme toho, že AnchorType je numerický Flag, kliknutí otočí svůj bit:
            int current = (int)anchorCurrent;
            int clicked = (int)anchorClick;
            int result = current ^ clicked;
            if (result == 0)
                result = (int)AnchorType.Both ^ clicked;
            return (AnchorType)result;
        }
        #endregion
        #region Selection
        /// <summary>
        /// Zajistí vložení / přidání této komponenty do seznamu komponent selectovaných v dokumentu.
        /// </summary>
        public void SelectComponentOne()
        {
            this.SelectComponentOne(false);
        }
        /// <summary>
        /// Zajistí vložení / přidání této komponenty do seznamu komponent selectovaných v dokumentu.
        /// </summary>
        /// <param name="withCtrl"></param>
        public void SelectComponentOne(bool withCtrl)
        {
            if (this.Document != null)
                this.Document.SelectComponentOne(this, withCtrl);
        }
        /// <summary>
        /// Určí, zda tato komponenta má být selectovaná, na základě shody svých souřadnic se souřadnicemi Frame (visualFrameArea).
        /// </summary>
        /// <param name="visualFrameArea"></param>
        /// <param name="selectFrameRatio"></param>
        /// <returns></returns>
        public bool IsSelectedInVisualFrame(RectangleF visualFrameArea, float selectFrameRatio)
        {
            if (!this.CurrentIsVisible) return false;
            if (!this.IsSelectable) return false;

            RectangleF currentArea = this.CurrentVisualArea;
            RectangleF intersectArea = currentArea;
            intersectArea.Intersect(visualFrameArea);
            return (intersectArea.GetArea() >= selectFrameRatio * currentArea.GetArea());
        }
        /// <summary>
        /// Stav, kdy je element selectován (this.IsSelected || this.IsSelectedFrame)
        /// </summary>
        public bool Selected { get { return (this.IsSelected || this.IsSelectedFrame); } }
        /// <summary>
        /// Obsahuje příznak true, že tento prvek lze selectovat (false = nejde to)
        /// </summary>
        public bool IsSelectable { get { return HasInteractiveModeAny(EditItemInteractivityMode.Select); } }
        /// <summary>
        /// Stav, kdy je element selectován
        /// </summary>
        public bool IsSelected { get; set; }
        /// <summary>
        /// Stav, kdy je element selectován výběrem, dočasně v průběhu framování.
        /// Po dokončení framování jsou framované komponenty označeny jako IsSelected = trvale selectované.
        /// </summary>
        public bool IsSelectedFrame { get; set; }
        /// <summary>
        /// Odselectuje tento prvek
        /// </summary>
        internal void SelectClear()
        {
            this.IsSelected = false;
            this.IsSelectedFrame = false;
        }
        #endregion
        #region Souřadnice logické (relativní a absolutní) a ukotvení k Bandům
        /// <summary>
        /// Příznak, že tato komponenta může mít rozsah přes více Bands. Default = false.
        /// Povolit na true je vhodné pouze u těch komponent, které to dokážou vykreslit. Typicky jde o rámečky, linky atd.
        /// Nevhodné je to pro texty, obrázky atd.
        /// </summary>
        public bool EnableMultiBand { get { return this.ReportObject.EnableMultiBand; } }
        /// <summary>
        /// Logické umístění prvku (v milimetrech).
        /// Umístění je míněno v rámci OwnerBand = relativně k počátku Band.
        /// </summary>
        [Browsable(false)]
        public RectangleF BoundsOld { get; set; }
        /// <summary>
        /// Logické relativní umístění prvku (v milimetrech).
        /// Umístění je relativní vzhledem k pozicím OwnerBandů (Top a Bottom).
        /// Z tohoto relativního umístění je možno vytvořit absolutní umístění pro aktuální souřadnice bandů a styly ukotvení AnchorXY, aniž by se měnil obsah RelativeBounds.
        /// </summary>
        [Browsable(false)]
        public RelativeRectangleF RelativeBounds { get; set; }
        /// <summary>
        /// Druh ukotvení v ose X
        /// </summary>
        [Browsable(false)]
        public AnchorType AnchorX { get; set; }
        /// <summary>
        /// Druh ukotvení v ose Y
        /// </summary>
        [Browsable(false)]
        public AnchorType AnchorY { get; set; }
        #endregion
        #region ZOrder - zatřídění prvku na danou hodnotu ZOrder, přepočet na korektní řadu, setřídění
        /// <summary>
        /// Umístění na ose Z.
        /// Nejnižší hodnota = 1 = úplně vespodu.
        /// Nejnovější prvek = úplně navrchu = nejvyšší hodnota.
        /// </summary>
        public int ZOrder { get; set; }
        /// <summary>
        /// Komparátor podle hodnoty ZOrder
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        internal static int CompareByZOrder(ReportComponent a, ReportComponent b)
        {
            if (a == null && b == null) return 0;
            if (a == null) return -1;
            if (b == null) return 1;
            return a.ZOrder.CompareTo(b.ZOrder);
        }
        /// <summary>
        /// Setřídí daný seznam prvků typu ReportComponent podle jejich hodnoty ZOrder (na tom není nic složitého).
        /// Neprovádí přečíslování seznamu, protože seznam nemusí obsahovat všechny prvky!
        /// </summary>
        /// <param name="list"></param>
        internal static void SortByZOrder(List<ReportComponent> list)
        {
            list.Sort(ReportComponent.CompareByZOrder);
        }
        /// <summary>
        /// Najde a vrátí nejvyšší hodnotu ZOrder z daného seznamu komponent.
        /// </summary>
        /// <param name="components"></param>
        /// <returns></returns>
        internal static Int32? FindMaxZOrder(IEnumerable<ReportComponent> components)
        {
            if (components.Any())
                return components.Max(c => c.ZOrder);
            return null;
        }
        #endregion
        #region Podpora pro interaktivní přemisťování komponenty
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši začíná přesouvat segment, na němž se nachází (this.CurrentActiveSegment).
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragBegin(EditInteractiveEventArgs args)
        {
            // Kdy budu přesouvat i okolní selectované objekty?
            // Pokud i já jsem selectovaný 
            //     (ono je ergonomicky nevhodné, abych přesouval selectované komponenty návazně s komponentou aktivní, ale neselectovanou)
            // a pokud přesouvám segment Body:
            bool moveSelected = (this.IsSelected && args.CurrentActiveSegment.GripType == EditorItemSegmentGripType.Body);
            this.Document.DragComponentsBegin(this, args, moveSelected);         // Skrz Document vyvolá metodu this.OnOtherDragComponentsBegin() v ostatních instancích, které se pohybu budou účastnit
            base.OnSegmentMouseLeftDragBegin(args);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši přesouvá segment, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragMove(EditInteractiveEventArgs args)
        {
            this.Document.DragComponentsStep(this, args);                        // Skrz Document vyvolá metodu this.OnOtherDragComponentsStep() v ostatních instancích, které se pohybu budou účastnit
            base.OnSegmentMouseLeftDragMove(args);
        }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši končí přesun segmentu, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnSegmentMouseLeftDragEnd(EditInteractiveEventArgs args)
        {
            this.Document.DragComponentsDoneBefore(this, args);                  // Zde se vyvolá zápis do Reverteru Prepare (před změnami)
            this.SetVisualBound(args, this.EditedCurrentVisualArea.Value);
            this.Document.DragComponentsDone(this, args);                        // Skrz Document vyvolá metodu this.OnOtherDragComponentsDone() v ostatních instancích, které se pohybu budou účastnit
            this.Document.DragComponentsDoneAfter(this, args);                   // Zde se vyvolá zápis do Reverteru AfterChange (po provedení změnam)
            base.OnSegmentMouseLeftDragEnd(args);
        }
        /// <summary>
        /// Událost, kterou volá dokument v situaci, kdy některá jiná komponenta začíná pohyb, a tato (this) komponenta je Selectována a měla by se pohybovat "vázaně".
        /// Nyní ještě nedošlo k pohybu, komponenta this je na původním místě, ale pohyb začne a je třeba se na něj připravit.
        /// </summary>
        /// <param name="activeComponent">Aktivní komponenta, s tou uživatel fyzicky hýbá</param>
        /// <param name="args"></param>
        public void OnOtherDragComponentsBegin(ReportComponent activeComponent, EditInteractiveEventArgs args)
        {
            // Převedu svoje aktuální vizuální souřadnice do souřadnic editačních, od této chvíle až do konce pohybu budou platit tyto:
            this.EditedInitialVisualArea = this.CurrentVisualArea;
            this.EditedCurrentVisualArea = this.CurrentVisualArea;
            this.CurrentMouseState = EditMouseState.MouseDragMove;
        }
        /// <summary>
        /// Událost, kterou volá dokument v situaci, kdy některá jiná komponenta začíná pohyb, a tato (this) komponenta je Selectována a měla by se pohybovat "vázaně".
        /// Nyní ještě nedošlo k pohybu, komponenta this je na původním místě, ale pohyb začne a je třeba se na něj připravit.
        /// </summary>
        /// <param name="activeComponent">Aktivní komponenta, s tou uživatel fyzicky hýbá</param>
        /// <param name="args"></param>
        public void OnOtherDragComponentsStep(ReportComponent activeComponent, EditInteractiveEventArgs args)
        {
            // Přepočtu svoje souřadnice tak, aby se upravily o tolik, o kolik se posunula myš od svého výchozího bodu posunu:
            PointF currentPoint = args.GetMovedPoint(this.EditedInitialVisualArea.Value.Location);
            this.EditedCurrentVisualArea = new RectangleF(currentPoint, this.EditedInitialVisualArea.Value.Size);
        }
        /// <summary>
        /// Událost, kterou volá dokument v situaci, kdy některá jiná komponenta začíná pohyb, a tato (this) komponenta je Selectována a měla by se pohybovat "vázaně".
        /// Nyní ještě nedošlo k pohybu, komponenta this je na původním místě, ale pohyb začne a je třeba se na něj připravit.
        /// </summary>
        /// <param name="activeComponent">Aktivní komponenta, s tou uživatel fyzicky hýbá</param>
        /// <param name="args"></param>
        public void OnOtherDragComponentsDone(ReportComponent activeComponent, EditInteractiveEventArgs args)
        {
            this.SetVisualBound(args, this.EditedCurrentVisualArea.Value);
            this.EditedInitialVisualArea = null;
            this.EditedCurrentVisualArea = null;
            this.CurrentMouseState = EditMouseState.Outside;
        }
        #endregion
        #region Podpora pro ReportObject : tvorba segmentů, a jejich vykreslování
        #region Tvorba segmentů: CreateStandardSegments()
        /// <summary>
        /// Vytvoří standardní sadu segmentů, odpovídající danému režimu interaktivity.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="editorLocked"></param>
        /// <param name="interactivityMode"></param>
        public void CreateStandardSegments(ReportObjectPrepareSegmentsArgs args, bool editorLocked, EditItemInteractivityMode interactivityMode)
        {
            switch (args.ObjectState)
            {
                case EditorObjectState.Select:
                case EditorObjectState.Insert:
                    this.CreateStandardSegmentsSelect(args, editorLocked, interactivityMode);
                    break;
                case EditorObjectState.Anchor:
                    this.CreateStandardSegmentsAnchor(args, editorLocked, interactivityMode);
                    break;
            }
        }
        /// <summary>
        /// Vytvoří standardní sadu segmentů, odpovídající danému režimu interaktivity a stavu editoru "Select"
        /// </summary>
        /// <param name="args"></param>
        /// <param name="editorLocked"></param>
        /// <param name="interactivityMode"></param>
        public void CreateStandardSegmentsSelect(ReportObjectPrepareSegmentsArgs args, bool editorLocked, EditItemInteractivityMode interactivityMode)
        {
            // Předvypočtu si vizuální souřadnice tohoto prvku:
            RectangleF visualBound = args.CurrentVisualBound;
            float xl = visualBound.X;
            float xw = visualBound.Width;
            float xc = visualBound.X + xw / 2f;
            float xr = visualBound.X + xw - 1f;
            float yt = visualBound.Y;
            float yh = visualBound.Height;
            float ym = visualBound.Y + yh / 2f;
            float yb = visualBound.Y + yh - 1f;

            bool withMouse = this.IsWithMouse;
            if (HasInteractiveModeAny(interactivityMode, EditItemInteractivityMode.Move))
            {	// Body:
                bool moveX = !editorLocked && HasInteractiveModeAny(interactivityMode, EditItemInteractivityMode.MoveX);
                bool moveY = !editorLocked && HasInteractiveModeAny(interactivityMode, EditItemInteractivityMode.MoveY);
                args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.Body, true,
                    new SheetF(xl - 1f, yt - 1f, xw + 2f, yh + 2f),
                    visualBound.Location,
                    visualBound,
                    SysCursorType.Default, SysCursorType.Default, SysCursorType.NoMoveAuto,
                    EditorItemSegmentGripType.Body,
                    moveX, moveY, false, moveX && moveY, false,
                    EditMouseChangeType.OnChange);
            }
            if (!editorLocked)
            {   // Segmenty připravím vždy (bez ohledu na přítomnost myši this.IsWithMouse):
                if (HasInteractiveModeAll(interactivityMode, EditItemInteractivityMode.ResizeX))
                {	// MiddleLeft:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.MiddleLeft, true,
                        new SheetF(xl - 5f, ym - 4f, 8f, 8f),
                        new PointF(xl, ym),
                        new RectangleF(xl - 4f, ym - 2f, 4f, 4f),
                        SysCursorType.SizeWE, SysCursorType.SizeWE, SysCursorType.SizeWE,
                        EditorItemSegmentGripType.MiddleLeft,
                        true, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // MiddleRight:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.MiddleRight, true,
                        new SheetF(xr - 2f, ym - 4f, 8f, 8f),
                        new PointF(xr, ym),
                        new RectangleF(xr + 1f, ym - 2f, 4f, 4f),
                        SysCursorType.SizeWE, SysCursorType.SizeWE, SysCursorType.SizeWE,
                        EditorItemSegmentGripType.MiddleRight,
                        true, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                }
                if (HasInteractiveModeAll(interactivityMode, EditItemInteractivityMode.ResizeY))
                {	// TopCenter:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.TopCenter, true,
                        new SheetF(xc - 4f, yt - 5f, 8f, 8f),
                        new PointF(xc, yt),
                        new RectangleF(xc - 2f, yt - 4f, 4f, 4f),
                        SysCursorType.SizeNS, SysCursorType.SizeNS, SysCursorType.SizeNS,
                        EditorItemSegmentGripType.TopCenter,
                        false, true, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // BottomCenter:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.BottomCenter, true,
                        new SheetF(xc - 4f, yb - 2f, 8f, 8f),
                        new PointF(xc, yb),
                        new RectangleF(xc - 2f, yb + 1f, 4f, 4f),
                        SysCursorType.SizeNS, SysCursorType.SizeNS, SysCursorType.SizeNS,
                        EditorItemSegmentGripType.BottomCenter,
                        false, true, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                }
                if (HasInteractiveModeAll(interactivityMode, EditItemInteractivityMode.Resize))
                {	// TopLeft:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.TopLeft, true,
                        new SheetF(xl - 5f, yt - 5f, 8f, 8f),
                        new PointF(xl, yt),
                        new RectangleF(xl - 5f, yt - 5f, 5f, 5f),
                        SysCursorType.SizeNWSE, SysCursorType.SizeNWSE, SysCursorType.SizeNWSE,
                        EditorItemSegmentGripType.TopLeft,
                        true, true, false, false, true,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // TopRight:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.TopRight, true,
                        new SheetF(xr - 2f, yt - 5f, 8f, 8f),
                        new PointF(xr, yt),
                        new RectangleF(xr + 1f, yt - 5f, 5f, 5f),
                        SysCursorType.SizeNESW, SysCursorType.SizeNESW, SysCursorType.SizeNESW,
                        EditorItemSegmentGripType.TopRight,
                        true, true, false, false, true,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // BottomRight:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.BottomRight, true,
                        new SheetF(xr - 2f, yb - 2f, 8f, 8f),
                        new PointF(xr, yb),
                        new RectangleF(xr + 1f, yb + 1f, 5f, 5f),
                        SysCursorType.SizeNWSE, SysCursorType.SizeNWSE, SysCursorType.SizeNWSE,
                        EditorItemSegmentGripType.BottomRight,
                        true, true, false, false, true,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // BottomLeft:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.BottomLeft, true,
                        new SheetF(xl - 5f, yb - 2f, 8f, 8f),
                        new PointF(xl, yb),
                        new RectangleF(xl - 5f, yb + 1f, 5f, 5f),
                        SysCursorType.SizeNESW, SysCursorType.SizeNESW, SysCursorType.SizeNESW,
                        EditorItemSegmentGripType.BottomLeft,
                        true, true, false, false, true,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                }

                if (!this.IsWithMouse)
                    // Pokud nemám myš, tak segmenty budou neviditelné - ale přesto aktivní.
                    // Tím zajistím, že na jejich (neviditelná ale očekávaná) místa lze myší najet, a ejhle - segment se zobrazí.
                    args.SetSegmentInvisible();
            }
        }
        /// <summary>
        /// Vytvoří standardní sadu segmentů, odpovídající danému režimu interaktivity a stavu editoru "Anchor"
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="editorLocked"></param>
        /// <param name="interactivityMode"></param>
        public void CreateStandardSegmentsAnchor(ReportObjectPrepareSegmentsArgs args, bool editorLocked, EditItemInteractivityMode interactivityMode)
        {
            // Předvypočtu si vizuální souřadnice tohoto prvku:
            RectangleF visualBound = args.CurrentVisualBound;
            float xl = visualBound.X;
            float xw = visualBound.Width;
            float xc = visualBound.X + xw / 2f;
            float xr = visualBound.X + xw - 1f;
            float yt = visualBound.Y;
            float yh = visualBound.Height;
            float ym = visualBound.Y + yh / 2f;
            float yb = visualBound.Y + yh - 1f;

            bool withMouse = this.IsWithMouse;
            if (HasInteractiveModeAny(interactivityMode, EditItemInteractivityMode.Anchor))
            {	// Body:
                args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.Body, true,
                    new SheetF(xl - 1f, yt - 1f, xw + 2f, yh + 2f),
                    visualBound.Location,
                    visualBound,
                    SysCursorType.Default, SysCursorType.Default, SysCursorType.NoMoveAuto,
                    EditorItemSegmentGripType.Body,
                    false, false, false, false, false,
                    EditMouseChangeType.OnChange);
            }
            if (!editorLocked)
            {   // Segmenty připravím vždy (bez ohledu na přítomnost myši this.IsWithMouse):
                if (HasInteractiveModeAll(interactivityMode, EditItemInteractivityMode.AnchorX))
                {	// MiddleLeft:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.MiddleLeft, true,
                        new SheetF(xl - 6f, ym - 5f, 10f, 10f),
                        new PointF(xl, ym),
                        new RectangleF(xl - 5f, ym - 3f, 6f, 6f),
                        SysCursorType.PanWest, SysCursorType.PanWest, SysCursorType.PanWest,
                        EditorItemSegmentGripType.MiddleLeft,
                        false, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // MiddleRight:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.MiddleRight, true,
                        new SheetF(xr - 3f, ym - 5f, 10f, 10f),
                        new PointF(xr, ym),
                        new RectangleF(xr + 0f, ym - 3f, 6f, 6f),
                        SysCursorType.PanEast, SysCursorType.PanEast, SysCursorType.PanEast,
                        EditorItemSegmentGripType.MiddleRight,
                        false, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                }
                if (HasInteractiveModeAll(interactivityMode, EditItemInteractivityMode.AnchorY))
                {	// TopCenter:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.TopCenter, true,
                        new SheetF(xc - 5f, yt - 6f, 10f, 10f),
                        new PointF(xc, yt),
                        new RectangleF(xc - 3f, yt - 5f, 6f, 6f),
                        SysCursorType.PanNorth, SysCursorType.PanNorth, SysCursorType.PanNorth,
                        EditorItemSegmentGripType.TopCenter,
                        false, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                    // BottomCenter:
                    args.PrepareInteractiveSegmentsOne(this.ReportObject, EditorItemSegmentGripType.BottomCenter, true,
                        new SheetF(xc - 5f, yb - 3f, 10f, 10f),
                        new PointF(xc, yb),
                        new RectangleF(xc - 3f, yb + 0f, 6f, 6f),
                        SysCursorType.PanSouth, SysCursorType.PanSouth, SysCursorType.PanSouth,
                        EditorItemSegmentGripType.BottomCenter,
                        false, false, false, false, false,
                        EditMouseChangeType.LeftDrag | EditMouseChangeType.EnterLeave);
                }

                if (!this.IsWithMouse)
                    // Pokud nemám myš, tak segmenty budou neviditelné - ale přesto aktivní.
                    // Tím zajistím, že na jejich (neviditelná ale očekávaná) místa lze myší najet, a ejhle - segment se zobrazí.
                    args.SetSegmentInvisible();
            }
        }
        /// <summary>
        /// Testuje, zda režim interaktivity (mode) obsahuje všechny požadované režimy (test).
        /// </summary>
        /// <param name="mode">Jaké režimy máme</param>
        /// <param name="test">Jaké režimy hledáme</param>
        /// <returns></returns>
        public static bool HasInteractiveModeAll(EditItemInteractivityMode mode, EditItemInteractivityMode test)
        {
            int currMode = (int)mode;
            int testMode = (int)test;
            return ((currMode & testMode) == testMode);
        }
        /// <summary>
        /// Testuje, zda režim interaktivity (mode) obsahuje alespoň jeden z požadovaných režimů (test).
        /// </summary>
        /// <param name="mode">Jaké režimy máme</param>
        /// <param name="test">Jaké režimy hledáme</param>
        /// <returns></returns>
        public static bool HasInteractiveModeAny(EditItemInteractivityMode mode, EditItemInteractivityMode test)
        {
            int currMode = (int)mode;
            int testMode = (int)test;
            return ((currMode & testMode) > 0);
        }
        #endregion
        #region Vykreslení segmentů: DrawStandardSegmentOne()
        /// <summary>
        /// Vykreslí daný segment
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        public void DrawStandardSegmentOne(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            if (segment.SegmentVisualBound.IsEmpty)
                return;

            switch (segment.GripType)
            {
                case EditorItemSegmentGripType.None:
                    break;
                case EditorItemSegmentGripType.Body:
                    break;
                case EditorItemSegmentGripType.TopCenter:
                case EditorItemSegmentGripType.BottomCenter:
                case EditorItemSegmentGripType.MiddleLeft:
                case EditorItemSegmentGripType.MiddleRight:
                    switch (args.ObjectState)
                    {
                        case EditorObjectState.Select:
                            this.DrawSegmentOneMiddleSelect(args, segment);
                            break;
                        case EditorObjectState.Anchor:
                            this.DrawSegmentOneMiddleAnchor(args, segment);
                            break;
                    }
                    break;
                case EditorItemSegmentGripType.TopLeft:
                case EditorItemSegmentGripType.TopRight:
                case EditorItemSegmentGripType.BottomRight:
                case EditorItemSegmentGripType.BottomLeft:
                    switch (args.ObjectState)
                    {
                        case EditorObjectState.Select:
                        case EditorObjectState.Insert:
                            this.DrawSegmentOneVertexSelect(args, segment);
                            break;
                        case EditorObjectState.Anchor:
                            this.DrawSegmentOneVertexAnchor(args, segment);
                            break;
                    }
                    break;
            }
        }
        /// <summary>
        /// Vrátí Brush pro segment určitého stavu
        /// </summary>
        /// <param name="colorType"></param>
        /// <returns></returns>
        public Brush GetBrushForSegment(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            SegmentColorType colorType = SegmentColorType.None;
            EditorItemSegmentGripType gripType = segment.GripType;
            switch (args.ObjectState)
            {
                case EditorObjectState.Select:
                    if (gripType == EditorItemSegmentGripType.TopCenter ||
                        gripType == EditorItemSegmentGripType.BottomCenter ||
                        gripType == EditorItemSegmentGripType.MiddleLeft ||
                        gripType == EditorItemSegmentGripType.MiddleRight)
                        colorType = SegmentColorType.SelectMiddle;
                    else
                        colorType = SegmentColorType.SelectVertex;
                    break;

                case EditorObjectState.Anchor:
                    bool isSegmentActive = IsAnchorActive(args, segment);
                    colorType = (isSegmentActive ? SegmentColorType.AnchorActive : SegmentColorType.AnchorPasive);
                    break;

                default:
                    colorType = SegmentColorType.None;
                    break;

            }
            return GetBrushForSegment(colorType);
        }
        /// <summary>
        /// Vrátí Brush pro segment určitého stavu
        /// </summary>
        /// <param name="colorType"></param>
        /// <returns></returns>
        public Brush GetBrushForSegment(SegmentColorType colorType)
        {
            return new SolidBrush(GetColorForSegment(colorType));
        }
        /// <summary>
        /// Vykreslí segment ležící uprostřed hrany (kreslí čtvereček)
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void DrawSegmentOneMiddleSelect(ReportObjectDrawArgs args, EditorItemSegment segment)
        {

            using (Brush brush = this.GetBrushForSegment(SegmentColorType.SelectMiddle))
            {
                args.Graphics.FillRectangle(brush, segment.SegmentVisualBound);
            }
        }
        /// <summary>
        /// Vykreslí segment ležící na rohu objektu (kreslí kolečko)
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void DrawSegmentOneVertexSelect(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            var state = args.Graphics.Save();
            args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            using (Brush brush = this.GetBrushForSegment(SegmentColorType.SelectVertex))
            {
                args.Graphics.FillEllipse(brush, segment.SegmentVisualBound);
            }
            args.Graphics.Restore(state);
        }
        /// <summary>
        /// Pro formu: kreslení kotvy na hraně (tam ale kotvy nejsou !)
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void DrawSegmentOneVertexAnchor(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            // V rozích není Anchor.
        }
        /// <summary>
        /// Vykreslí segmenty v režimu Anchor = grafické znázornění ukotvení objektu v rámci bandu
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        private void DrawSegmentOneMiddleAnchor(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            bool isSegmentActive = IsAnchorActive(args, segment);
            SegmentColorType colorType = (isSegmentActive ? SegmentColorType.AnchorActive : SegmentColorType.AnchorPasive);
            var state = args.Graphics.Save();
            args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            using (Brush brush = this.GetBrushForSegment(colorType))
            {
                float angle = GetAngleForAnchor(args, segment);

                // Kotva v objektu:
                Point point1 = Point.Round(segment.SegmentVisualBound.Center());
                Rectangle area1 = point1.CreateRectangleFromCenter(10);
                args.Graphics.FillPie(brush, area1, angle, 180f);

                // Kotva protilehlá na hraně OwnerBandu, a linka (jen pokud je kotva aktivní):
                if (isSegmentActive)
                {
                    PointF? bandPoint = this.GetOwnerPointForAnchor(segment);
                    if (bandPoint != null)
                    {
                        Point point2 = Point.Round(bandPoint.Value);
                        using (Pen pen = new Pen(GetColorForSegment(colorType), 1f))
                        {
                            pen.DashStyle = DashStyle.Custom;
                            pen.DashPattern = new float[] { 1f, 2f, 1f, 3f };
                            args.Graphics.DrawLine(pen, point1, point2);
                        }
                    }
                }
            }
            args.Graphics.Restore(state);
        }
        /// <summary>
        /// Vrátí souřadnice bodu, který je "magnetem" pro kotvu dle segmentu = bod ležící na odpovídající hraně prostoru Bandu
        /// </summary>
        /// <param name="segment"></param>
        /// <returns></returns>
        private PointF? GetOwnerPointForAnchor(EditorItemSegment segment)
        {
            ReportBand ownerBand = ((segment.GripType == EditorItemSegmentGripType.BottomCenter && this.OwnerBottomBandId != 0 && this.OwnerBottomBandId != this.OwnerTopBandId) ? this.OwnerBottomBand : this.OwnerTopBand);
            if (ownerBand == null) return null;
            RectangleF? ownerArea = ownerBand.CurrentRowVisualBound;
            if (ownerArea == null) return null;
            PointF anchorPoint = segment.SegmentVisualBound.Center();
            SizeF anchorSize = segment.SegmentVisualBound.Size;
            if (anchorSize.Width <= 0f || anchorSize.Height <= 0f) return null;
            switch (segment.GripType)
            {
                case EditorItemSegmentGripType.TopCenter:
                    return new PointF(anchorPoint.X, ownerArea.Value.Y);
                case EditorItemSegmentGripType.BottomCenter:
                    return new PointF(anchorPoint.X, ownerArea.Value.Bottom);
                case EditorItemSegmentGripType.MiddleLeft:
                    return new PointF(ownerArea.Value.X, anchorPoint.Y);
                case EditorItemSegmentGripType.MiddleRight:
                    return new PointF(ownerArea.Value.Right, anchorPoint.Y);
            }
            return null;
        }
        /// <summary>
        /// Vrátí barvu pro segment určitého stavu
        /// </summary>
        /// <param name="colorType"></param>
        /// <returns></returns>
        public Color GetColorForSegment(SegmentColorType colorType)
        {
            switch (colorType)
            {
                case SegmentColorType.SelectMiddle: return Color.DarkBlue;
                case SegmentColorType.SelectVertex: return Color.DarkBlue;
                case SegmentColorType.AnchorPasive: return Color.DarkBlue;
                case SegmentColorType.AnchorActive: return Color.Red;
                default: return Color.Black;
            }
        }
        /// <summary>
        /// Vrací úhel, pod kterým se začíná kreslit segment Middle ve stavu Anchor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        public float GetAngleForAnchor(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            switch (segment.GripType)
            {
                case EditorItemSegmentGripType.TopCenter: return 0f;
                case EditorItemSegmentGripType.BottomCenter: return 180f;
                case EditorItemSegmentGripType.MiddleLeft: return 270f;
                case EditorItemSegmentGripType.MiddleRight: return 90f;
            }
            return 0f;
        }
        /// <summary>
        /// Vrací příznak, zda kotva pro daný segment je u aktuální komponenty aktivní
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
        public bool IsAnchorActive(ReportObjectDrawArgs args, EditorItemSegment segment)
        {
            AnchorType anchorX = this.AnchorX;
            AnchorType anchorY = this.AnchorY;
            switch (segment.GripType)
            {
                case EditorItemSegmentGripType.TopCenter: return (anchorY == AnchorType.Begin || anchorY == AnchorType.Both);
                case EditorItemSegmentGripType.BottomCenter: return (anchorY == AnchorType.After || anchorY == AnchorType.Both);
                case EditorItemSegmentGripType.MiddleLeft: return (anchorX == AnchorType.Begin || anchorX == AnchorType.Both);
                case EditorItemSegmentGripType.MiddleRight: return (anchorX == AnchorType.After || anchorX == AnchorType.Both);
            }
            return false;
        }
        #endregion
        #endregion
        #region Serializace
        /// <summary>
        /// Statický konstruktor konkrétní instance, která je popsána v daném XML elementu.
        /// Jde o object factory metodu, vrací konkrétního potomka (podle hodnoty atributu Type).
        /// Pokud nelze objekt vytvořit, vrací null.
        /// </summary>
        /// <param name="document">Majitel = dokument</param>
        /// <param name="node">XML element</param>
        /// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
        /// <returns></returns>
        public static ReportComponent XmlDeserializeFrom(ReportDocument document, XElement node, bool overrideId)
        {
            if (node == null) return null;

            XAttribute typeAtt = node.Attribute("Type");
            if (typeAtt == null || String.IsNullOrEmpty(typeAtt.Value)) return null;

            IReportObject reportObject = ReportObjects.ReportObject.GetObject(typeAtt.Value);
            if (reportObject == null) return null;

            ReportComponent component = new ReportComponent(document, reportObject);
            component.XmlFillFrom(node, overrideId);

            return component;
        }
        /// <summary>
        /// Vytvoří a vrátí novou komponentu, která bude obsahovat ReportObject daného typu.
        /// Object factory metoda.
        /// </summary>
        /// <param name="document"></param>
        /// <param name="objectType"></param>
        /// <returns></returns>
        public static ReportComponent Create(ReportDocument document, Type objectType)
        {
            IReportObject reportObject = ReportObjects.ReportObject.GetObject(objectType);
            if (reportObject == null) return null;

            ReportComponent component = new ReportComponent(document, reportObject);

            return component;
        }

        /// <summary>
        /// Načte do sebe veškerá data z XML nodu.
        /// Volitelně přepíše i svoje vlastní ID (pokud je overrideId = true). To se používá při čtení z XML dokumentu (Load, Revert, Undo, Redo).
        /// V případě Cut/Copy + Paste se overrideId předává false = vzniká nový téměř identický objekt k objektu původnímu, ale s novým ID.
        /// </summary>
        /// <param name="node">XML element</param>
        /// <param name="overrideId">Požadavek na načtení i vlastního ID (true = načíst / false = ponechat stávající)</param>
        public virtual void XmlFillFrom(XElement node, bool overrideId)
        {
            if (overrideId)
                this.ComponentId = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "ComponentId"));
            this.OwnerTopBandId = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "BandId"));
            int bottomBandId = (Int32)Convertor.StringToInt32(ReportDocument.XmlGetXAttributeValue(node, "BandEndId"));
            if (bottomBandId != 0)
                this.OwnerBottomBandId = bottomBandId;
            this.XmlOwnerTopBandName = (string)ReportDocument.XmlGetXAttributeValue(node, "BandIdName");
            this.XmlOwnerBottomBandName = (string)ReportDocument.XmlGetXAttributeValue(node, "BandEndIdName");
            this.RelativeBounds = RelativeRectangleF.FromXmlSerialData((string)ReportDocument.XmlGetXAttributeValue(node, "RelativeBounds"));
            this.AnchorX = (AnchorType)Convertor.StringToEnum(ReportDocument.XmlGetXAttributeValue(node, "AnchorX"), AnchorType.Begin);
            this.AnchorY = (AnchorType)Convertor.StringToEnum(ReportDocument.XmlGetXAttributeValue(node, "AnchorY"), AnchorType.Begin);

            this.ReportObject.XmlFillFrom(node);
        }
        /// <summary>
        /// Zajistí serializaci všech svých hodnot do předaného XML elementu.
        /// Virtuální metoda.
        /// Element vytváří nadřízená vrstva (ReportDocument), úkolem komponenty je uložit do XML elementu všechny své hodnoty.
        /// Každá třída ukládá své hodnoty, před tím volá base.XmlSerializeTo(node), aby předkové uložili svoje data.
        /// </summary>
        /// <param name="node"></param>
        public virtual void XmlSerializeTo(XElement node)
        {
            this.XmlSerializeTo(node, false);
        }
        /// <summary>
        /// Zajistí serializaci všech svých hodnot do předaného XML elementu.
        /// Virtuální metoda.
        /// Element vytváří nadřízená vrstva (ReportDocument), úkolem komponenty je uložit do XML elementu všechny své hodnoty.
        /// Každá třída ukládá své hodnoty, před tím volá base.XmlSerializeTo(node), aby předkové uložili svoje data.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="extendedAttributes">Ukládat i rozšířené atributy (true = pro Copy + Paste, false = pro Save a Load)</param>
        public virtual void XmlSerializeTo(XElement node, bool extendedAttributes)
        {
            node.SetAttributeValue("Type", ReportObjects.ReportObject.GetTypeName(this.ReportObject));
            node.SetAttributeValue("ComponentId", Convertor.Int32ToString(this.ComponentId));
            node.SetAttributeValue("BandId", Convertor.Int32ToString(this.OwnerTopBandId));
            if (this.IsMultiBand)
                node.SetAttributeValue("BandEndId", Convertor.Int32ToString(this.OwnerBottomBandId));
            if (extendedAttributes)
            {
                node.SetAttributeValue("BandIdName", Convertor.StringToString(this.OwnerTopBandName));
                if (this.IsMultiBand)
                    node.SetAttributeValue("BandEndIdName", Convertor.StringToString(this.OwnerBottomBandName));
            }
            node.SetAttributeValue("RelativeBounds", this.RelativeBounds.XmlSerialData);
            if (this.AnchorX == AnchorType.After || this.AnchorX == AnchorType.Both)
                node.SetAttributeValue("AnchorX", Convertor.EnumToString(this.AnchorX));
            if (this.AnchorY == AnchorType.After || this.AnchorY == AnchorType.Both)
                node.SetAttributeValue("AnchorY", Convertor.EnumToString(this.AnchorY));

            this.ReportObject.XmlSerializeTo(node, extendedAttributes);
        }
        #endregion
        #region IRevertItem Members
        IComparable IRevertItem.ObjectId
        {
            get { return new ItemId(ItemType.Component, this.ComponentId); }
        }
        string IRevertItem.GetData()
        {
            XElement element = new XElement(ReportDocument.XML_COMPONENT);
            this.XmlSerializeTo(element);
            return element.ToString();
        }
        void IRevertItem.SetData(string data)
        {
            XElement element = XElement.Parse(data);
            this.XmlFillFrom(element, true);
        }
        #endregion
    }
    #region interface IReportObject
    /// <summary>
    /// interface IReportObject : předepisuje vlastnosti pro objekt, který může být použit jako objekt reportu
    /// </summary>
    public interface IReportObject : IPlugin, IMenuItem
    {
        /// <summary>
        /// Majitel tohoto objektu = objekt ReportComponent, který zprostředkovává vazby na dokument, editor, záhlaví a další.
        /// </summary>
        ReportComponent Owner { get; set; }
        /// <summary>
        /// Jméno komponenty uváděné v nabídce komponent nad property gridem
        /// </summary>
        string ComponentName { get; }
        /// <summary>
        /// Příznak, že tato komponenta může mít rozsah přes více Bands. Default = false.
        /// Povolit na true je vhodné pouze u těch komponent, které to dokážou vykreslit. Typicky jde o rámečky, linky atd.
        /// Nevhodné je to pro texty, obrázky atd.
        /// </summary>
        bool EnableMultiBand { get; }
        /// <summary>
        /// Property udává možnosti interaktivity tohoto prvku.
        /// Na základě hodnoty v této property pak prvek deklaruje aktivní segmenty a jejich vlastnosti, 
        /// viz metoda this.PrepareSegments().
        /// Bázová třída obsahuje hodnotu StandardActiveResize, 
        /// potomek ji může předefinovat (property je virtual), a bázová třída pak v metodě PrepareSegments() deklaruje odpovídající segmenty.
        /// </summary>
        EditItemInteractivityMode InteractivityMode { get; }
        /// <summary>
        /// Aktuální vizuální souřadnice na ploše editoru.
        /// Jsou nastaveny před kreslením, pokud dochází k jejich změně.
        /// Na změny souřadnice prvek nemá reagovat voláním ReDraw(), to by mohlo vést ke zpomalení nebo zacyklení editoru.
        /// Prvek sám by je neměl měnit, nedává to smysl.
        /// Jsou zde uloženy pro potřeby prvku.
        /// </summary>
        RectangleF EditorVisualBounds { get; set; }
        /// <summary>
        /// Zámek editace pozice objektu.
        /// Editor (uživatel) může některé objekty zamknout, pak se tyto objekty nepohybují ani neresizují, a to ani při hromadných změnách.
        /// Prvek na to reaguje vizuálně: nezobrazuje odpovídající úchyty (Segmenty), nepodporuje přesouvání (Body).
        /// </summary>
        bool EditorLocked { get; set; }
        /// <summary>
        /// Voláno v době editace.
        /// Umožní prvku, aby korigoval souřadnice a/nebo velikost prvku.
        /// </summary>
        /// <param name="args"></param>
        void EditorLogicalBoundsChange(ReportObjectLogicalBoundsChangeArgs args);
        /// <summary>
        /// Voláno v době editace.
        /// Připraví a do argumentu vloží soupis svých segmentů, které chce v dané chvíli zobrazit.
        /// V argumentu dostává informace o prostoru prvku a jeho stavu, do argumentu přidává jednotlivé segmenty.
        /// </summary>
        /// <param name="args"></param>
        void EditorPrepareInteractiveSegments(ReportObjectPrepareSegmentsArgs args);
        /// <summary>
        /// Voláno v době editace.
        /// Prvek může reagovat na aktuální aktivitu myši.
        /// </summary>
        /// <param name="args"></param>
        void EditorSegmentInteractivity(ReportObjectInteractiveSegmentsArgs args);
        /// <summary>
        /// Voláno v době editace.
        /// Prvek vykreslí sebe (pozadí, popředí), svůj stav Selected, a všechny svoje připravené segmenty.
        /// </summary>
        /// <param name="args"></param>
        void EditorPaint(ReportObjectDrawArgs args);
        /// <summary>
        /// Zajistí serializaci všech svých hodnot do předaného XML elementu.
        /// Element vytváří nadřízená vrstva (ReportDocument), úkolem komponenty je uložit do XML elementu všechny své hodnoty.
        /// Každá třída ukládá své hodnoty, před tím volá base.XmlSerializeTo(node), aby i předkové uložili svoje data.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="extendedAttributes">Ukládat i rozšířené atributy (true = pro Copy + Paste, false = pro Save a Load)</param>
        void XmlSerializeTo(XElement node, bool extendedAttributes);
        /// <summary>
        /// Načte do sebe veškerá svoje data z XML nodu.
        /// </summary>
        /// <param name="node">XML element</param>
        void XmlFillFrom(XElement node);
    }
    #endregion
    #region interface IMenuItem; class MenuItemInfo; enum MenuItemAvailability
    /// <summary>
    /// interface IMenuItem : předepisuje vlastnosti pro objekt, který chce být zobrazen v UI
    /// </summary>
    public interface IMenuItem
    {
        /// <summary>
        /// Property, která obsahuje veškeré informace pro tvorbu UI
        /// </summary>
        MenuItemInfo MenuInfo { get; }
    }
    /// <summary>
    /// Informace, které vrací objekt který chce být vidět v menu nebo toolbaru.
    /// Na základě těchto informací lze vytvořit položku UI.
    /// </summary>
    public class MenuItemInfo
    {
        /// <summary>
        /// Dostupnost prvku v toolbaru v editoru.
        /// Pokud prvek není v editoru dostupný, nemůže být přidán do nového reportu, ani nemůže být zkopírován přes schránku.
        /// Nicméně existující prvek bude plně funkční, a to i v editoru, včetně modifikace vlastností.
        /// </summary>
        public MenuItemAvailability Availability { get; set; }
        /// <summary>
        /// Skupina prvků v toolbaru v editoru. Nejde o DropDownButton, ale o skupiny buttonů oddělené Separatorem.
        /// Skupiny jsou řazeny názvem, abecedně. Název skupiny se nikde nevypisuje.
        /// </summary>
        public string GroupName { get; set; }
        /// <summary>
        /// Pořadí prvku v toolbaru v editoru, v rámci své skupiny EditorToolstripGroupName.
        /// </summary>
        public int OrderInGroup { get; set; }
        /// <summary>
        /// Název prvku v toolbaru v editoru. Uvádí se v prvním řádku Tooltipu, a v režimu Button+Název.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Rozšiřující informační text v toolbaru v editoru.
        /// </summary>
        public string TooltipText { get; set; }
        /// <summary>
        /// Ikona v toolbaru v editoru, v režimu Malé ikony.
        /// </summary>
        public Image ImageSmall { get; set; }
        /// <summary>
        /// Ikona v toolbaru v editoru, v režimu Střední ikony.
        /// </summary>
        public Image ImageMedium { get; set; }
        /// <summary>
        /// Komparátor, který setřídí položky MenuItemInfo podle skupiny (GroupName) a podle pořadí (OrderInGroup).
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static int CompareMenuInfo(MenuItemInfo a, MenuItemInfo b)
        {
            if (a == null || b == null) return 0;

            string aGrp = a.GroupName;
            string bGrp = b.GroupName;
            if (aGrp == null && bGrp != null) return -1;
            if (aGrp != null && bGrp == null) return 1;
            if (aGrp != null && bGrp != null)
            {
                int cmp = String.Compare(aGrp, bGrp, StringComparison.InvariantCultureIgnoreCase);
                if (cmp != 0) return cmp;
            }
            return a.OrderInGroup.CompareTo(b.OrderInGroup);
        }
    }
    /// <summary>
    /// Režim dostupnosti prvku v editoru reportu.
    /// </summary>
    public enum MenuItemAvailability
    {
        /// <summary>
        /// Prvek se nenabízí v editoru nikomu.
        /// </summary>
        None,
        /// <summary>
        /// Prvek se nabízí v editoru pouze vývojářům (LCS + partneři).
        /// </summary>
        Developers,
        /// <summary>
        /// Prvek se nabízí v editoru vývojářům a správcům.
        /// </summary>
        Administrators,
        /// <summary>
        /// Prvek se nabízí v editoru všem.
        /// </summary>
        Users
    }
    #endregion
    #region Třídy argumentů ReportObjectArgs pro interface IReportObject
    /// <summary>
    /// Bázová třída argumentů pro interface IReportObject
    /// </summary>
    public class ReportObjectArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="component"></param>
        public ReportObjectArgs(EditEventArgs args, ReportComponent component)
        {
            this.Args = args;
            this.Component = component;
        }
        public EditEventArgs Args { get; private set; }
        /// <summary>
        /// Konvertor logických a vizuálních souřadnic
        /// </summary>
        public IViewPortConvertor ViewPortConvertor { get { return this.Args.ViewPortConvertor; } }
        /// <summary>
        /// Stav editoru z hlediska práce s objekty (Select, Anchor, 
        /// </summary>
        public EditorObjectState ObjectState { get { return this.Args.ObjectState; } }
        /// <summary>
        /// Komponenta
        /// </summary>
        public ReportComponent Component { get; private set; }
        /// <summary>
        /// Aktuální vizuální souřadnice objektu
        /// </summary>
        public RectangleF CurrentVisualBound { get { return this.Component.CurrentVisualArea; } }
        /// <summary>
        /// Stav myši
        /// </summary>
        public EditMouseState MouseState { get { return this.Component.CurrentMouseState; } }
        /// <summary>
        /// Stav Selected tohoto prvku.
        /// </summary>
        public bool Selected { get { return this.Component.Selected; } }
        /// <summary>
        /// Příznak true, pokud je aktuálně na tomto objektu myš (MouseState je OnControl nebo MouseLeftDown nebo MouseRightDown)
        /// </summary>
        public bool IsWithMouse { get { return (this.MouseState == EditMouseState.OnControl || this.MouseState == EditMouseState.MouseLeftDown || this.MouseState == EditMouseState.MouseRightDown || this.MouseState == EditMouseState.MouseDragMove || this.MouseState == EditMouseState.MouseDragResize); } }
    }
    /// <summary>
    /// ReportObjectLogicalBoundsChangeArgs : data pro metodu, která může spolupracovat při určení logických rozměrů objektu
    /// </summary>
    public class ReportObjectLogicalBoundsChangeArgs : ReportObjectArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="component"></param>
        /// <param name="changeType"></param>
        /// <param name="logicalBounds"></param>
        public ReportObjectLogicalBoundsChangeArgs(EditEventArgs args, ReportComponent component, LogicalBoundsChangeType changeType, RectangleF? logicalBounds)
            : base(args, component)
        {
            this.ChangeType = changeType;
            this.LogicalBounds = logicalBounds;
        }
        /// <summary>
        /// Druh (důvod) změny rozměrů
        /// </summary>
        public LogicalBoundsChangeType ChangeType { get; private set; }
        /// <summary>
        /// Logické souřadnice prvku : při vstupu dané editorem, metoda je může změnit a editor je bude akceptovat.
        /// S výjimkou změny ChangeType = Insert, kdy z ergonomických důvodů není vhodné měnit umístění (Location), takže se převezme pouze Size.
        /// Pokud je na výstupu null, editor ponechá v platnosti původní souřadnice.
        /// </summary>
        public RectangleF? LogicalBounds { get; set; }
    }
    /// <summary>
    /// Režim změny logických souřadnic
    /// </summary>
    public enum LogicalBoundsChangeType
    {
        /// <summary>
        /// Není změna
        /// </summary>
        None = 0,
        /// <summary>
        /// Vkládání komponenty
        /// </summary>
        Insert,
        /// <summary>
        /// Přesun komponenty
        /// </summary>
        Move,
        /// <summary>
        /// Změna velikosti komponenty
        /// </summary>
        Resize
    }
    /// <summary>
    /// ReportObjectPrepareSegmentsArgs : data pro metodu, která připravuje seznam interaktivních segmentů tohoto prvku.
    /// </summary>
    public class ReportObjectPrepareSegmentsArgs : ReportObjectArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="component"></param>
        public ReportObjectPrepareSegmentsArgs(EditEventArgs args, ReportComponent component)
            : base(args, component)
        {
            this.Segments = new List<EditorItemSegment>();
        }
        /// <summary>
        /// Výsledný seznam segmentů. Sem metoda umisťuje interaktivní prvky sebe sama.
        /// Interaktivní prvky jsou: Body (když prvek sám je interaktivní, lze jej posunout nebo na něj kliknout), krajové úchyty (když jej lze resizovat), a User (když se o ně bude starat objekt sám).
        /// Typ se ukládá do GripType.
        /// Seznam je vytvořen (není null).
        /// </summary>
        public List<EditorItemSegment> Segments { get; private set; }
        #region Podpora pro tvorbu segmentů
        /// <summary>
        /// Z daných parametrů sestaví data o jednom segmentu a přidá je do this.Segments
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="segmentType">Typ segmentu = jeho klíč. Tato hodnota vyhledá existující segment v mapě segmentů.</param>
        /// <param name="isActive">Má být segment aktivní?</param>
        /// <param name="interactiveSheet">Interaktivné souřadnice = kde segment reaguje na myš.</param>
        /// <param name="segmentPoint">Klíčový bod segmentu = ten se pohybuje při pohybu myši.</param>
        /// <param name="visualBound">Vizuální souřadnice, využívá potomek, nebo implicitní kreslení známých gripů.</param>
        /// <param name="mouseCursor">Kurzor při pohybu myši nad segmentem.</param>
        /// <param name="downCursor">Kurzor při stiskutí myši na segmentu.</param>
        /// <param name="dragCursor">Kurzor při tažení segmentu. Může být *Auto, pak reaguje na konkrétní pohyb.</param>
        /// <param name="gripType">Typ segmentu z hlediska jeho funkce.</param>
        /// <param name="autoDragX">Povoleno automatické přemisťování ve směru X.</param>
        /// <param name="autoDragY">Povoleno automatické přemisťování ve směru Y.</param>
        /// <param name="restrictRatio">Zachovat Ratio = poměr stran prvku</param>
        /// <param name="restrictXyCtrl">Umožnit omezení pohybu na směr X nebo Y, klávesou CTRL</param>
        /// <param name="restrictRatioCtrl">Umožnit zachování Ratio, klávesou CTRL</param>
        /// <param name="drawOnAction">Souhrn aktivit myši, po kterých je vyžádáno překreslení interaktivní vrstvy</param>
        public void PrepareInteractiveSegmentsOne(object owner, IComparable segmentType, bool isActive, SheetF interactiveSheet, PointF segmentPoint, RectangleF visualBound,
            SysCursorType mouseCursor, SysCursorType downCursor, SysCursorType dragCursor, EditorItemSegmentGripType gripType,
            bool autoDragX, bool autoDragY, bool restrictRatio, bool restrictXyCtrl, bool restrictRatioCtrl,
            EditMouseChangeType drawOnAction)
        {
            EditorItemSegment segment = new EditorItemSegment(owner);
            segment.SegmentType = segmentType;
            segment.IsActive = isActive;
            segment.SegmentInteractiveSheet = interactiveSheet;
            segment.SegmentPoint = segmentPoint;
            segment.MovedSegmentPoint = segmentPoint;
            segment.SegmentVisualBound = visualBound;
            segment.OnMouseMoveCursorType = mouseCursor;
            segment.OnMouseDownCursorType = downCursor;
            segment.OnMouseDragRequestCursorType = dragCursor;
            segment.GripType = gripType;
            segment.EnableAutoDragX = autoDragX;
            segment.EnableAutoDragY = autoDragY;
            segment.EnableRestrictRatio = restrictRatio;
            segment.EnableAutoRestrictXYbyCtrl = restrictXyCtrl;
            segment.EnableAutoRestrictRatioByCtrl = restrictRatioCtrl;
            segment.DrawOnAction = drawOnAction;

            this.Segments.Add(segment);
        }
        /// <summary>
        /// Nastaví všechny segmenty jako neviditelné.
        /// Segmenty budou aktivní, ale nebudou se zobrazovat.
        /// </summary>
        internal void SetSegmentInvisible()
        {
            foreach (EditorItemSegment segment in this.Segments)
                segment.SetInvisible();

        }
        /// <summary>
        /// Nastaví vybrané segmenty jako neviditelné.
        /// Segmenty budou aktivní, ale nebudou se zobrazovat.
        /// </summary>
        /// <param name="predicate"></param>
        internal void SetSegmentInvisible(Func<EditorItemSegment,bool> predicate)
        {
            foreach (EditorItemSegment segment in this.Segments.Where(s => predicate(s)))
                segment.SetInvisible();

        }
        #endregion
    }
    /// <summary>
    /// ReportObjectInteractiveSegmentsArgs : data pro metody, které řeší interaktivu segmentů tohoto prvku.
    /// </summary>
    public class ReportObjectInteractiveSegmentsArgs : ReportObjectArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="component"></param>
        /// <param name="onMouseSegment"></param>
        public ReportObjectInteractiveSegmentsArgs(EditInteractiveEventArgs args, ReportComponent component, EditorItemSegment onMouseSegment)
            : base(args, component)
        {
            this.MouseChange = args.MouseChange;
            this.MousePointF = args.MousePointF;
            this.MousePointFDown = args.MousePointFDown;
            this.Segments = component.Segments;
            this.ActiveSegment = component.CurrentActiveSegment;
            this.OnMouseSegment = onMouseSegment;
            this.NeedCursor = SysCursorType.Null;
        }
        /// <summary>
        /// Režim změny stavu myši
        /// </summary>
        public EditMouseChangeType MouseChange { get; private set; }
        /// <summary>
        /// Bod, na kterém se nachází myš.
        /// Koordináty = pixely controlu
        /// </summary>
        public PointF MousePointF { get; private set; }
        /// <summary>
        /// Bod, na kterém se nacházela myš, když se stiskla.
        /// Je vyplněno pouze v událostech, kde je myš stisknutá anebo se zvedá (MouseUp).
        /// Koordináty = pixely controlu
        /// </summary>
        public PointF MousePointFDown { get; private set; }
        /// <summary>
        /// Obsahuje ve formě PointF posun aktuální pozice myši (MousePointF) oproti bodu, kde byla stisknuta (MousePointFDown).
        /// Při posunu doleva obsahuje zápornou souřadnici X, atd.
        /// </summary>
        public PointF MouseMoveOffset
        {
            get { return new PointF(this.MousePointF.X - this.MousePointFDown.X, this.MousePointF.Y - this.MousePointFDown.Y); }
        }
        /// <summary>
        /// Metoda vrátí daný bod posunutý shodně, jako se posunula myš (z bodu MousePointFDown do bodu MousePointF).
        /// </summary>
        /// <param name="originalPoint"></param>
        /// <returns></returns>
        public PointF GetMovedPoint(PointF originalPoint)
        {
            PointF offset = this.MouseMoveOffset;
            return new PointF(originalPoint.X + offset.X, originalPoint.Y + offset.Y);
        }
        /// <summary>
        /// Seznam segmentů. Tyto segmenty má prvek vykreslit.
        /// </summary>
        public IEnumerable<EditorItemSegment> Segments { get; private set; }
        /// <summary>
        /// Aktuální segment, metoda jej může změnit.
        /// Typicky segment, který byl aktivován myší minule, a nyní se s ním pohybuje, nebo na kterém jsme před chvilkou stáli a nyní z něj odcházíme.
        /// Metoda by měla například provádět toto:
        /// při příchodu na segment jej načte z OnMouseSegment, a vloží jej do ActiveSegment;
        /// při stisknutí myši jej obdobně načte z OnMouseSegment, a vloží jej do ActiveSegment;
        /// při pohybu myši při stisknutém tlačítku jej nechá beze změny v ActiveSegment a pohybuje s ním; atd.
        /// </summary>
        public EditorItemSegment ActiveSegment { get; set; }
        /// <summary>
        /// Segment, který je nyní pod myší. V některých metodách to není relevantní 
        /// (typicky při přesunu segmentu myší může být v OnMouseSegment uveden segment, nad kterým se pohybuji, ale segment který se myší pohybuje je uveden stále v ActiveSegment).
        /// </summary>
        public EditorItemSegment OnMouseSegment { get; private set; }
        /// <summary>
        /// Požadavek na Draw všech objektů v celém editoru
        /// </summary>
        public bool ReDrawAll { get; set; }
        /// <summary>
        /// Požadavek na Draw všech komponent
        /// </summary>
        public bool ReDrawComponents { get; set; }
        /// <summary>
        /// Požadavek na Draw objektů na interaktivní vrstvě
        /// </summary>
        public bool ReDrawInteractive { get; set; }
        /// <summary>
        /// Požadovaný druh kurzoru
        /// </summary>
        public SysCursorType NeedCursor { get; set; }
        /// <summary>
        /// Příznak, že tato konkrétní událost byla ošetřena (true), a že se nemá volat výchozí obsluha této události.
        /// </summary>
        public bool Handled { get; set; }
        /// <summary>
        /// Uloží svoje výstupní data do argumentu třídy EditInteractiveEventArgs
        /// </summary>
        /// <param name="args"></param>
        internal void StoreResultTo(EditInteractiveEventArgs args)
        {
            if (this.ReDrawAll)
                args.ReDrawLayer(EditorLayer.Ground);
            else if (this.ReDrawComponents)
                args.ReDrawLayer(EditorLayer.Components);
            else if (this.ReDrawInteractive)
                args.ReDrawLayer(EditorLayer.Interactive);

            args.NeedCursor = this.NeedCursor;
        }
    }
    /// <summary>
    /// ReportObjectDrawArgs : data pro metodu, která vykresluje tento prvek reportu.
    /// </summary>
    public class ReportObjectDrawArgs : ReportObjectArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="component"></param>
        public ReportObjectDrawArgs(EditPaintEventArgs args, ReportComponent component)
            : base(args, component)
        {
            this.Graphics = args.Graphics;
            this.Segments = component.Segments;
        }
        /// <summary>
        /// Grafika, do níž má prvek kreslit.
        /// </summary>
        public Graphics Graphics { get; private set; }
        /// <summary>
        /// Seznam segmentů. Tyto segmenty má prvek vykreslit.
        /// </summary>
        public IEnumerable<EditorItemSegment> Segments { get; private set; }
    }
    /// <summary>
    /// Typ barvy pro segment
    /// </summary>
    public enum SegmentColorType
    {
        None,
        SelectMiddle,
        SelectVertex,
        AnchorPasive,
        AnchorActive
    }
    #endregion
}